docs: rebuild the v2 documentation around a task-based page tree#2397
Conversation
Lay out the 45-page docs-v2 tree: authoring conventions in _meta/CONVENTIONS.md, a one-line-per-page index in _TREE.md, H2-outline scaffolds for 39 pages, and byte-identical copies of the three migration guides.
Fully write index, get-started/first-server, and servers/tools. Their code fences are region-synced from new examples/guides companion files that typecheck in the examples workspace.
Moves the docs-v2 scaffold into docs/ proper, wires the 45-page sidebar, keeps the existing guide pages under a collapsed 'Current guides (being replaced)' group, and replaces the README-generated landing with the calibration index. Dead-link checking is suspended on this branch only while most pages are scaffolds.
- sessions-state-scaling: rename the three H2s to imperative micro-steps (Pin a client to a session / Resume a dropped stream / Scale across nodes) so the page matches the rest of the serving how-to recipes - legacy-clients: rewrite the serveStdio 'reject' sentence in active voice - web-standard: stop telling the reader to export the guarded handler as the default; the 'Run it and verify' step curls the default export from 127.0.0.1, which the Host allowlist would 403, contradicting the quoted output
Remove docs/server.md, docs/client.md, docs/server-quickstart.md,
docs/client-quickstart.md, and docs/faq.md now that the page tree under
docs/get-started, docs/servers, docs/serving, docs/clients,
docs/protocol-versions.md, and docs/troubleshooting.md replaces them, and
drop the "Current guides (being replaced)" sidebar group.
Repoint every inbound reference:
- README.md: the Getting Started bullets now point at
docs/get-started/first-server.md and first-client.md, and the
Documentation section links the tutorials, the documentation site, and
docs/troubleshooting.md in place of the Server Guide / Client Guide /
FAQ bullets.
- packages/server/README.md and packages/client/README.md (embedded into
the typedoc API pages) link to the published site instead of the
deleted GitHub pages.
- packages/server/src/server/requestStateCodec.ts: the Web Crypto error
message now points at the troubleshooting page (which carries the
Node.js polyfill entry) instead of docs/faq.md.
- docs/migration/{index,upgrade-to-v2,support-2026-07-28}.md: replace the
faq.md / server.md / client.md links with their live equivalents.
- examples/README.md, examples/guides/README.md, the quickstart story
READMEs, and examples/oauth-client-credentials/README.md describe and
link the live pages.
- docs/_meta/CONVENTIONS.md: the illustrative scaffold comment and the
live sync-checked fence reference live files.
Also remove examples/guides/serverGuide.examples.ts and
clientGuide.examples.ts: their only consumers were the deleted guide
pages.
The README's status block now says beta, asks for feedback through a new v2-labeled issue form, and points the doc links at the documentation site. The PR-restriction warning is reworded without the stale date.
…nt rules The guide companions are documentation snippets: filenames mirror their page slugs and every region must stay self-contained when rendered, so the example-app style rules do not apply to them. Formatting the files re-syncs the doc fences that mirror their regions.
|
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
CI typechecks the workspace without building the packages first, so the examples package resolves @modelcontextprotocol/* imports through its tsconfig paths; the validator subpaths used by the schema-libraries guide companion were missing from that map and only resolved locally via built dists.
…ession The sessions routing example returned 404 for both an unknown Mcp-Session-Id and a request that lacked the header entirely; the latter is a malformed request and gets 400, matching examples/legacy-routing.
The guide companions and the contributor conventions still referenced the docs-v2/ working directory the pages were drafted in; the page tree index duplicated (and already contradicted) the sidebar, so it is removed; the root CLAUDE.md entry for examples/guides described the retired snippet collections.
| --- | ||
| status: calibration | ||
| shape: landing | ||
| --- |
There was a problem hiding this comment.
🟡 Three published pages — docs/index.md, docs/get-started/first-server.md, and docs/servers/tools.md — still carry the internal drafting-pipeline frontmatter key status: calibration (defined only in docs/_meta/CONVENTIONS.md as a writing-workflow marker); no other shipped page has a status: key. VitePress ignores unknown frontmatter, so nothing breaks, but it is internal-process metadata in public page sources — drop the status: calibration line from all three pages, keeping the shared shape: key.
Extended reasoning...
What it is. grep '^status:' docs/**/*.md matches exactly three files in the tree this PR ships: docs/index.md:2, docs/get-started/first-server.md:2, and docs/servers/tools.md:2, each carrying status: calibration in its frontmatter. No other published page has a status: key, while the shape: key (also in those frontmatter blocks) is shared by every page in the tree.
Where the key comes from. docs/_meta/CONVENTIONS.md, added in this same PR, defines status: as a drafting-pipeline marker: scaffold pages get status: scaffold, and the calibration pages were the Felix-approved samples used to calibrate the writing register ("every non-calibration page is a SKELETON"). It is a writing-workflow state field, not a documented or rendered VitePress frontmatter field. _meta/ itself is excluded from the published site via srcExclude, but these three pages are not — they are the landing page, the first tutorial, and the Tools page, the most-visited pages in the new tree.
Why nothing catches it. VitePress silently ignores unknown frontmatter keys, so pnpm docs:build passes and the rendered pages look identical with or without the line. Only someone reading the page sources (or a contributor copying a page's frontmatter as a template for a new page) sees it.
Why it matters anyway. This is exactly the class of drafting residue the PR's own final commit ("docs: scrub stale drafting references from the companions and meta docs") removed elsewhere, so the three remaining status: calibration lines contradict that cleanup and the rest of the tree's frontmatter convention. A contributor who copies one of these pages as a starting point would propagate an internal-process key with no meaning outside the drafting workflow.
Step-by-step proof. (1) Run grep -rn '^status:' docs --include='*.md' at the head commit — it returns only the three files above. (2) Open docs/_meta/CONVENTIONS.md and find the SCAFFOLD FORMAT block: the frontmatter template is status: scaffold / shape: <…>, confirming status: is a drafting-state field. (3) Open any other shipped page (e.g. docs/servers/resources.md) — its frontmatter has only shape:. (4) Build the site: VitePress renders all three pages identically whether the line is present or not, confirming the key is dead metadata.
Fix. Delete the status: calibration line from the frontmatter of docs/index.md, docs/get-started/first-server.md, and docs/servers/tools.md, keeping the shape: key each page shares with the rest of the tree. No other change is needed; this should not block the merge.
| NEVER run bare `pnpm sync:snippets` — it rewrites every fenced block in `docs/` and | ||
| `docs/` in place, and concurrent mutating runs from parallel agents race and clobber |
There was a problem hiding this comment.
🟡 Lines 232-233 read "it rewrites every fenced block in docs/ and docs/ in place" — the same directory is named twice, a leftover from the mechanical docs-v2/ → docs/ replacement (the original sentence enumerated docs/ and docs-v2/). Drop the duplicate so it reads "every fenced block in docs/ in place".
Extended reasoning...
What it is. docs/_meta/CONVENTIONS.md lines 232-233, in section "5. The sync command (HARD RULE)", currently read: "NEVER run bare pnpm sync:snippets — it rewrites every fenced block in docs/ and docs/ in place, and concurrent mutating runs from parallel agents race and clobber each other." The sentence names the same directory twice in one breath.
Where it came from. The earlier review round flagged stale docs-v2/ path references throughout this file and the companion-file headers. The final commit (docs: scrub stale drafting references from the companions and meta docs, 4aa9554) fixed those by mechanically replacing docs-v2/ with docs/ — and this sentence originally enumerated two distinct trees ("in docs/ and docs-v2/"), so the replacement collapsed it into the duplicated phrase. The file now contains zero docs-v2 occurrences, confirming the substitution; this duplication is the one artifact the scrub left behind, and it is not covered by the existing claude[bot] comment (which described the pre-scrub state, since fixed).
The code path / where readers hit it. CONVENTIONS.md presents itself as the "Single source of truth for form" that "every writer agent reads … before touching a page," and this particular sentence is a HARD RULE about which command contributors must never run. A garbled rule sentence in the contributors' source-of-truth document is exactly the kind of prose-vs-reality drift the rest of this PR takes care to avoid.
Why nothing catches it. docs/_meta/** is in srcExclude in docs/.vitepress/config.mts, so the file never reaches the published site and no build, link check, or snippet check looks at its prose. Only a human reading the conventions notices.
Step-by-step proof. (1) Open docs/_meta/CONVENTIONS.md at the head commit and go to section 5. (2) Line 232-233 reads "…rewrites every fenced block in docs/ and docs/ in place…". (3) grep -c docs-v2 docs/_meta/CONVENTIONS.md returns 0, so the second docs/ cannot be referring to a second tree — it is the residue of docs-v2/ after the find/replace. (4) The sync script (scripts/sync-snippets.ts) scans only docs/**/*.md, so the corrected sentence should name a single tree.
Impact and fix. No reader-facing or runtime impact — purely contributor-facing prose — hence nit. The fix is a one-phrase edit: change the sentence to "it rewrites every fenced block in docs/ in place, and concurrent mutating runs from parallel agents race and clobber each other."
| - **[Repository README](https://github.com/modelcontextprotocol/typescript-sdk#readme)** — overview, package layout, examples | ||
| - **[Client guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/client.md)** | ||
| - **[Client guide](https://ts.sdk.modelcontextprotocol.io/v2/clients/connect)** — connecting, calling tools, OAuth, and middleware | ||
| - **[API reference](https://ts.sdk.modelcontextprotocol.io/v2/)** |
There was a problem hiding this comment.
🟡 The 'API reference' bullets in packages/client/README.md:25 and packages/server/README.md:29 still link to https://ts.sdk.modelcontextprotocol.io/v2/, which after this PR is the hand-written docs landing page rather than the typedoc reference. Both should point at https://ts.sdk.modelcontextprotocol.io/v2/api/, matching the root README updated in this same PR and the docs/index.md fix in a401b35.
Extended reasoning...
What the bug is. Both package READMEs end with a small link list, and in each one the bullet labeled API reference points at https://ts.sdk.modelcontextprotocol.io/v2/ (packages/client/README.md:25, packages/server/README.md:29). Before this PR, /v2/ was the README-rendered landing of the docs site, so the label was loosely accurate. After this PR, /v2/ is the new hand-written content landing page (docs/index.md, added here), and the typedoc-generated API reference lives one level deeper at /v2/api/ — see the nav entry { text: 'API Reference', link: '/api/' } in docs/.vitepress/config.mts combined with base: '/v2/'.
Why it is in scope for this PR. Both files are touched by this diff: the Client guide / Server guide bullets directly above these lines are repointed to the new page tree. The PR also updates the root README.md to link API reference correctly to https://ts.sdk.modelcontextprotocol.io/v2/api/ (README.md:147), and the head commit a401b35 fixed the identical mistake on docs/index.md (now ./api/). The two package README bullets are the only remaining API reference labels that still point at the landing page, so the inconsistency is introduced/left behind by this PR's redefinition of what /v2/ is.
Impact. These READMEs are what npm renders for @modelcontextprotocol/client and @modelcontextprotocol/server. A user who clicks API reference lands on the docs home instead of the generated reference and has to find the API Reference nav entry themselves. No runtime impact, and pnpm docs:build's dead-link check cannot catch it because the URL is an absolute external URL that does resolve — just to the wrong page.
Step-by-step proof. (1) Open packages/client/README.md:25: the href is https://ts.sdk.modelcontextprotocol.io/v2/. (2) With base: '/v2/' in docs/.vitepress/config.mts, that URL renders docs/index.md — the landing page added by this PR — not the typedoc output. (3) The reference itself is published at /v2/api/, which is exactly where the root README's API reference bullet (updated in this same diff) and the fixed docs/index.md link now point. (4) So the package README link's label and target disagree, while every other API reference link in the repo agrees on /v2/api/.
Fix. Change the URL in both bullets to https://ts.sdk.modelcontextprotocol.io/v2/api/:
- **[API reference](https://ts.sdk.modelcontextprotocol.io/v2/api/)**One-line change in each of packages/client/README.md and packages/server/README.md.
Rebuilds the v2 documentation content on the VitePress site introduced in #2395. The two ~1,000-line guides, the two quickstarts, and the FAQ are replaced by a task-based page tree — get started, servers, serving, clients, protocol versions, advanced, testing, troubleshooting — with one job per page. Every code block on every page is included from a typecheck-gated companion example, the runnable companions are executed in CI, and the quoted outputs on the pages are the outputs those programs produce.
Motivation and Context
#2395 replaced the typedoc site with a real documentation site but deliberately kept the existing content. This is the content half: pages organized around what the reader is doing instead of two monolithic guides, written code-first, with the API reference staying a generated section rather than the site.
The migration guides are carried over with only link retargeting.
How Has This Been Tested?
tsfence is a//#regionof a companion file underexamples/guides/; the examples workspace typechecks them against the SDK, andpnpm sync:snippets --checkfails if a page drifts from its source region.pnpm docs:examplesstep (wired into the examples CI job) executes every runnable companion — 37 programs, 6 typecheck-only — so behavior shown on a page is exercised on every PR, not just compiled. Quoted outputs on the pages are taken from those runs.pnpm docs:buildpasses with dead-link checking enabled: 0 dead links, 0 typedoc warnings, 191 pages.pnpm --filter @modelcontextprotocol/server testpasses (one error-message string changed).Breaking Changes
None at runtime. Five documentation URLs are removed in favor of the new tree (
/v2/server,/v2/client,/v2/server-quickstart,/v2/client-quickstart,/v2/faq), without redirects. Inbound references (README, package READMEs, migration guides, examples README, one error-message URL in@modelcontextprotocol/server) are repointed.Types of changes
Checklist
Additional context
v2-labeled issue form (.github/ISSUE_TEMPLATE/v2-feedback.yml); a small Examples page in Get started links the example library.protocol-versions) that everything else links to.examples/guides/{server,client}Guide.examples.tsare deleted with the pages they fed; the runnable example stories underexamples/<story>/are untouched.